import type { StyleElementType, StyleTagOptions } from '../index'
import { logger } from '../../../types/constants'
import { StyleInjectPosition, SYMBOLS } from '../../../types/interfaces'
import { ToolKit } from '../../utils/toolkit'
import { StyleBaseInjector } from '../base'

/**
 * Shadow DOM 样式注入器：用于 Web Components 和 Shadow DOM 环境
 */
export class ShadowInjector extends StyleBaseInjector {
  readonly name: string = 'ShadowInjector'
  readonly styleTags: Map<string, { style: HTMLStyleElement, shadowRoot: ShadowRoot }> = new Map()

  destroy(): void {
    this.styleTags.forEach(({ style }) => style.remove())
    this.styleTags.clear()
  }

  createStyleTag(
    id: string,
    content: string,
    options?: StyleTagOptions,
  ): HTMLStyleElement | null {
    const {
      targetElement,
      onlyCreate = false,
      allowEmpty = false,
      allowUpdate = true,
    } = options || {}

    const existingStyle = this.getStyleTag(id)
    if (existingStyle) {
      if (allowUpdate) {
        this.updateStyle(id, content, options)
      }
      return existingStyle
    }

    if (!this.checkShadowRootValidity(targetElement))
      return null

    if (!this.checkContentValidity(id, content, onlyCreate, allowEmpty))
      return null

    const style = document.createElement('style')
    style.id = this.generateStyleId(id)
    style.setAttribute('data-scope', 'shadow-dom')

    if (onlyCreate) {
      logger.debug(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} 创建 Shadow DOM 空样式标签: id='${id}'`)
      this.styleTags.set(id, { style, shadowRoot: targetElement as ShadowRoot })
      return style
    }

    style.textContent = this.processStyleContent(content)
    this.injectStyle(style, options)

    return style
  }

  injectStyle(style: HTMLStyleElement, options: StyleTagOptions = {}): boolean {
    const { targetElement, override = true, position = StyleInjectPosition.Last } = options

    if (!this.checkShadowRootValidity(targetElement))
      return false

    const id = style.id
    const shadowRoot = targetElement as ShadowRoot

    try {
      // 检查是否已存在同 ID 样式
      const existingStyle = this.getStyleTag(id, false)

      if (existingStyle) {
        if (!override) {
          logger.hint(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} Shadow DOM 样式已存在且未启用覆盖，跳过注入: id='${id}'`)
          return false
        }
        existingStyle.remove()
        logger.hint(`[${this.name}] 已移除旧 Shadow DOM 样式: id='${id}'`)
      }

      const firstChild = shadowRoot.firstChild
      const defaultInsertHandler = () => {
        firstChild
          ? shadowRoot.insertBefore(style, shadowRoot.firstChild)
          : shadowRoot.appendChild(style)
      }

      // 根据位置注入
      switch (position) {
        case StyleInjectPosition.First:
          defaultInsertHandler()
          break
        case StyleInjectPosition.Last:
          shadowRoot.appendChild(style)
          break
        default:
          defaultInsertHandler()
          break
      }

      this.styleTags.set(id, { style, shadowRoot })
      ToolKit.ui.style.markBaiduSearchElement(style)
      logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} Shadow DOM 样式注入成功: id='${id}'`)
      return true
    }
    catch (error) {
      logger.error(`[${this.name}]  Shadow DOM 样式注入失败: id='${id}'`, error)
      return false
    }
  }

  updateStyle(id: string, content: string, _options: StyleTagOptions = {}): boolean {
    const styleInfo = this.styleTags.get(id)

    if (!styleInfo) {
      logger.warn(`[${this.name}] Shadow DOM 样式不存在: id='${id}'`)
      return false
    }

    styleInfo.style.textContent = this.processStyleContent(content)
    logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} Shadow DOM 样式更新成功: id='${id}'`)
    return true
  }

  /**
   * 获取 Shadow Root 中的所有样式标签
   */
  getStylesInShadowRoot(shadowRoot: ShadowRoot): HTMLStyleElement[] {
    return Array.from(shadowRoot.querySelectorAll('style'))
  }

  /**
   * 检查 Shadow Root 是否有效
   */
  checkShadowRootValidity(shadowRoot: StyleElementType | undefined): boolean {
    if (!shadowRoot || !(shadowRoot instanceof ShadowRoot)) {
      logger.error(`[${this.name}] Shadow DOM 目标无效`)
      return false
    }

    return true
  }

  getStyleTag(id: string, needsProcessing: boolean = true): HTMLStyleElement | null {
    const styleId = needsProcessing ? this.generateStyleId(id) : id
    return this.styleTags.get(styleId)?.style || null
  }

  generateStyleId(id: string): string {
    return `shadow-${id}`
  }
}
